package betower

import (
	"context"
	"database/sql"
	"encoding/json"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
	"strconv"
	"strings"
	"sync"
	"time"
	"wzgames/internal/constant"
	"wzgames/internal/gdao/aviatordao"
	"wzgames/internal/gdao/roudao"
	"wzgames/internal/gdao/stmdao"
	"wzgames/internal/gloader"
	"wzgames/internal/gmq/grq"
	"wzgames/internal/model/aviatorp"
	"wzgames/internal/model/mcomm"
	"wzgames/internal/model/roulettep"
	"wzgames/internal/model/statement"
	"wzgames/internal/response"
	"wzgames/pkg/common/delayqueue"
	"wzgames/pkg/components/grabbitmq"
	"wzgames/pkg/components/mysql"
	"wzgames/pkg/components/redis"
	"wzgames/pkg/logger"
	"wzgames/pkg/utils/byteutil"
	"wzgames/pkg/utils/dateutil"
)

// 投注派彩日志
// 流水生成

var (
	stmMgr  *StmManager
	onceStm sync.Once
)

const (
	defaultTimeout = time.Second * 2
)

type StmManager struct {
	stmDs    *mysql.DS
	walletDs *mysql.DS
	avtDs    *mysql.DS
	rouDs    *mysql.DS
	redisC   *redis.RedisClient
	ctx      context.Context
	cancel   context.CancelFunc

	betsChan   chan grabbitmq.Delivery
	avBetsChan chan grabbitmq.Delivery
	settleChan chan grabbitmq.Delivery

	betsKey  string
	delayQue *delayqueue.DelayQueue
}

func StmMgr() *StmManager {
	if stmMgr == nil {
		onceStm.Do(func() {
			stmMgr = &StmManager{
				betsChan:   make(chan grabbitmq.Delivery, 5),
				avBetsChan: make(chan grabbitmq.Delivery, 5),
				settleChan: make(chan grabbitmq.Delivery, 5),
			}
			stmMgr.stmDs = gloader.CommMgr().GetDBProxy(constant.MysqlOfBHistory)
			stmMgr.walletDs = gloader.CommMgr().GetDBProxy(constant.MysqlOfWallet)
			stmMgr.avtDs = gloader.CommMgr().GetDBProxy(constant.MysqlOfAviator)
			stmMgr.rouDs = gloader.CommMgr().GetDBProxy(constant.MysqlOfRoulette)
			stmMgr.redisC = gloader.CommMgr().RedisPool()
			stmMgr.ctx, stmMgr.cancel = context.WithCancel(context.Background())

			var bd strings.Builder
			bd.WriteString(gloader.EnvString())
			bd.WriteString(constant.CacheBetIdKey)
			stmMgr.betsKey = bd.String()

			//stmMgr.delayQue = delayqueue.NewQueue(stmMgr.betsKey, stmMgr.redisC.Get(), stmMgr.DelayQueCallback).WithConcurrent(4)
		})
	}

	return stmMgr
}

func (w *StmManager) Init() error {
	// RabbitMQ 消费G8和新轮盘的投注、派彩、投注取消等信息
	_, err := grq.MqMgr().CreateConsumer(constant.ConsumerImBet,
		[]*grabbitmq.ExchangeBinds{
			{
				Exch: grabbitmq.DefaultExchange(grq.DefaultExchange, grabbitmq.ExchangeTopic, nil),
				Bindings: []*grabbitmq.Binding{
					{
						RouteKey: constant.SubjectBetsWild,
						Queues: []*grabbitmq.Queue{
							grabbitmq.DefaultQueue(constant.QueueBets, nil),
						},
					},
				},
			},
		}, w.betsChan)
	if err != nil {
		return err
	}
	// RabbitMQ 消费小飞机的投注、派彩、取消
	_, err = grq.MqMgr().CreateConsumer(constant.ConsumerAvtBet,
		[]*grabbitmq.ExchangeBinds{
			{
				Exch: grabbitmq.DefaultExchange(grq.DefaultExchange, grabbitmq.ExchangeTopic, nil),
				Bindings: []*grabbitmq.Binding{
					{
						RouteKey: constant.SubjectAvtWild,
						Queues: []*grabbitmq.Queue{
							grabbitmq.DefaultQueue(constant.QueueAvtBets, nil),
						},
					},
				},
			},
		}, w.avBetsChan)
	if err != nil {
		return err
	}
	// RabbitMQ 消费结算信息，并生成流水
	_, err = grq.MqMgr().CreateConsumer(constant.ConsumerSettle,
		[]*grabbitmq.ExchangeBinds{
			{
				Exch: grabbitmq.DefaultExchange(grq.DefaultExchange, grabbitmq.ExchangeTopic, nil),
				Bindings: []*grabbitmq.Binding{
					{
						RouteKey: constant.SubjectSettleWild,
						Queues: []*grabbitmq.Queue{
							grabbitmq.DefaultQueue(constant.QueueSettle, nil),
						},
					},
				},
			},
		}, w.settleChan)
	if err != nil {
		return err
	}

	go w.consumeBetsChan()
	go w.consumeAvtBetsChan()
	go w.consumeSettleChan()

	//w.delayQue.StartConsume()

	return nil
}

func (w *StmManager) consumeBetsChan() {
	for {
		select {
		case msg := <-w.betsChan:
			if w.consumeImBets(msg.RoutingKey, msg.Body) {
				msg.Ack(false)
			}
		case <-w.ctx.Done():
			return
		}
	}
}

func (w *StmManager) consumeAvtBetsChan() {
	for {
		select {
		case msg := <-w.avBetsChan:
			if w.consumeAvtStepBets(msg.RoutingKey, msg.Body) {
				msg.Ack(false)
			}
		case <-w.ctx.Done():
			return
		}
	}
}

func (w *StmManager) consumeSettleChan() {
	for {
		select {
		case msg := <-w.settleChan:
			if w.consumeSettle(msg.RoutingKey, msg.Body) {
				msg.Ack(false)
			}
		case <-w.ctx.Done():
			return
		}
	}
}

func (w *StmManager) consumeImBets(routeKey string, data []byte) bool {
	if routeKey == "" {
		return true
	}
	defer logger.Recover()
	switch routeKey {
	case constant.SubjectImBet: // 即时结算的投注，如Dice、Keno等
		return w.processBet(routeKey, data)
	case constant.SubjectBetPayout: // 派彩日志
		return w.processPayout(routeKey, data)
	case constant.SubjectStepBet: // 分步操作日志，如Mines、HiLo、Goal等
		return w.processBetStep(routeKey, data)
	case constant.SubjectBetIpt: // 投注或分步操作的原始输入数据
		return w.processBetIpt(routeKey, data)
	case constant.SubjectBetCancel: // 投注取消
		return w.processBetCancel(routeKey, data)
	}
	logger.Warn("[BetTower] consume im bets fail on Unknown routeKey", zap.String("Subject", routeKey))
	return false
}

func (w *StmManager) processBet(routeKey string, data []byte) bool {
	bet := &statement.PlayBet{}
	err := proto.Unmarshal(data, bet)
	if err != nil {
		logger.Error("[BetTower] consume im bet fail on proto.Unmarshal", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	t := dateutil.UTC()
	betEnt := &statement.PlayBetEnt{
		BetId:     bet.BetId,
		RoundId:   bet.RoundId,
		Uid:       bet.Uid,
		NodeId:    bet.NodeId,
		AgtId:     bet.AgtId,
		CurrId:    bet.CurrId,
		InstId:    bet.InstId,
		GameId:    bet.GameId,
		Precision: bet.Precision,
		Curr:      bet.Curr,
		FreeBet:   bet.FreeBet,
		BetAmt:    bet.BetAmt,
		EffAmt:    bet.EffAmt,
		Fee:       bet.Fee,
		BetTime:   bet.Dateline,
		GcId:      bet.GcId,
		Uname:     bet.Uname,
		AgtUid:    bet.AgtUid,
		Detail:    bet.Detail,
		Created:   t,
		Updated:   t,
	}
	err = dao.CreatePlayBet(betEnt)
	if err != nil {
		logger.Error("[BetTower] save play bet fail on CreatePlayBet", zap.Uint64("BetId", bet.BetId), zap.Error(err))
		return false
	}
	if bet.GameAct == mcomm.GameAction_GameImResult && bet.ImRet != nil {
		payOutEnt := &statement.PlayPayoutEnt{
			BetId:   bet.BetId,
			RoundId: bet.ImRet.RoundId,
			GAct:    bet.ImRet.GameAct,
			Win:     bet.ImRet.Win,
			WinAmt:  bet.ImRet.WinAmt,
			Payout:  bet.ImRet.Payout,
			PayTime: bet.ImRet.Dateline,
			Op:      bet.ImRet.Op,
			Detail:  bet.ImRet.Detail,
			Created: t,
			Updated: t,
		}
		err = dao.CreateBetPayout(payOutEnt)
		if err != nil {
			logger.Error("[BetTower] save play bet pay out fail on CreateBetPayout", zap.Uint64("BetId", bet.BetId), zap.Error(err))
			return false
		}
	}

	return true
}

func (w *StmManager) processPayout(routeKey string, data []byte) bool {
	bet := &statement.PlayPayout{}
	err := proto.Unmarshal(data, bet)
	if err != nil {
		logger.Error("[BetTower] consume bet payout fail on proto.Unmarshal", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	t := dateutil.UTC()
	payOutEnt := &statement.PlayPayoutEnt{
		BetId:   bet.BetId,
		RoundId: bet.RoundId,
		GAct:    bet.GameAct,
		Win:     bet.Win,
		WinAmt:  bet.WinAmt,
		Payout:  bet.Payout,
		PayTime: bet.Dateline,
		Op:      bet.Op,
		Detail:  bet.Detail,
		Created: t,
		Updated: t,
	}
	err = dao.CreateBetPayout(payOutEnt)
	if err != nil {
		logger.Error("[BetTower] save play bet pay out fail on CreateBetPayout", zap.Uint64("BetId", bet.BetId), zap.Error(err))
		return false
	}

	return true
}

func (w *StmManager) genDelayQueKey(betId string) string {
	var bd strings.Builder
	bd.WriteString(gloader.EnvString())
	bd.WriteString(constant.CacheDelayQueKey)
	bd.WriteString(betId)
	bd.WriteString(constant.Colon)
	bd.WriteString(strconv.FormatInt(dateutil.UnixMilli(), 10))

	return bd.String()
}

func (w *StmManager) DelayQueCallback(data string) bool {
	return true
}

func (w *StmManager) processBetStep(routeKey string, data []byte) bool {
	bet := &statement.PlayStep{}
	err := proto.Unmarshal(data, bet)
	if err != nil {
		logger.Error("[BetTower] consume bet step fail on proto.Unmarshal", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	stepEnt := &statement.PlayStepEnt{
		BetId:    bet.BetId,
		RoundId:  bet.RoundId,
		Win:      bet.Win,
		WinAmt:   bet.WinAmt,
		Payout:   bet.Payout,
		Dateline: bet.Dateline,
		Detail:   bet.Detail,
	}
	err = dao.CreateBetStep(stepEnt)
	if err != nil {
		logger.Error("[BetTower] save play bet step fail on CreateBetStep", zap.Uint64("BetId", bet.BetId), zap.Error(err))
		return false
	}

	return true
}

func (w *StmManager) processBetIpt(routeKey string, data []byte) bool {
	bet := &statement.PlayBet{}
	err := proto.Unmarshal(data, bet)
	if err != nil {
		logger.Error("[BetTower] consume bet input fail on proto.Unmarshal", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	iptEnt := &statement.PlayInputData{
		Uid:       bet.Uid,
		Dateline:  bet.Dateline,
		NodeId:    bet.NodeId,
		CurrId:    bet.CurrId,
		InstId:    bet.InstId,
		GameId:    bet.GameId,
		Precision: bet.Precision,
		BetId:     bet.BetId,
		RoundId:   bet.RoundId,
		GAct:      bet.GameAct,
		CkCode:    bet.CkCode,
		Balance:   bet.Balance,
		Data:      bet.Detail,
	}
	err = dao.CreateInputData(iptEnt)
	if err != nil {
		logger.Error("[BetTower] save play bet input fail on CreateBetStep", zap.Uint64("Uid", bet.Uid), zap.Uint64("BetId", bet.BetId), zap.Error(err))
		return false
	}

	return true
}

func (w *StmManager) processBetCancel(routeKey string, data []byte) bool {
	bet := &statement.PlayCancel{}
	err := proto.Unmarshal(data, bet)
	if err != nil {
		logger.Error("[BetTower] consume bet cancel fail on proto.Unmarshal", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	cancelEnt := &statement.PlayCancelEnt{
		BetId:    bet.BetId,
		Op:       bet.Op,
		Dateline: bet.Dateline,
	}
	err = dao.CreateBetCancel(cancelEnt)
	if err != nil {
		logger.Error("[BetTower] save play bet cancel fail on CreateBetCancel", zap.Uint64("BetId", bet.BetId), zap.Error(err))
		return false
	}

	return true
}

func (w *StmManager) consumeAvtStepBets(routeKey string, data []byte) bool {
	if routeKey == "" {
		return true
	}
	defer logger.Recover()
	switch routeKey {
	case constant.SubjectAvtBet: // 小飞机投注
		return w.processBet(routeKey, data)
	case constant.SubjectAvtPayout: // 小飞机派彩（CashOut)
		return w.processPayout(routeKey, data)
	case constant.SubjectAvtCancel: // 小飞机取消下注
		return w.processBetCancel(routeKey, data)
	}

	logger.Warn("[BetTower] consume avt step bets fail on Unknown routeKey", zap.String("Subject", routeKey))
	return false
}

func (w *StmManager) consumeSettle(routeKey string, data []byte) bool {
	if routeKey == "" {
		return true
	}
	defer logger.Recover()
	switch routeKey {
	case constant.SubjectSettleStm: // 常规投注结算
		return w.processBetSettle(routeKey, data)
	case constant.SubjectAvtLoseStm: // 小飞机输的结算
		return w.processAvtBetLoseSettle(routeKey, data)
	}

	logger.Warn("[BetTower] consume settle fail on Unknown routeKey", zap.String("Subject", routeKey))
	return false
}

func (w *StmManager) processBetSettle(routeKey string, data []byte) bool {
	ss := &statement.SettleStmList{}
	err := proto.Unmarshal(data, ss)
	if err != nil {
		logger.Error("[BetTower] consume bet settle fail on proto.Unmarshal", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}
	ids, c := w.genSettleSqlIds(ss)
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	// TODO 批量查询投注日志
	bets, c1, e1 := dao.GetPlayBetIn(ids)
	if e1 != nil {
		logger.Error("[BetTower] consume bet settle fail on GetPlayBetIn", zap.String("Subject", routeKey), zap.Error(e1))
		return false
	}
	if c1 != c {
		//logger.Warn("[BetTower] not equal", zap.Int("C", c), zap.Int("C1", c1), zap.String("ss", ids))
		return false
	}
	// 批量查询派彩日志
	payouts, c2, e2 := dao.GetPlayPayoutIn(ids)
	if e2 != nil {
		logger.Error("[BetTower] consume bet settle fail on GetPlayPayoutIn", zap.String("Subject", routeKey), zap.Error(e2))
		return false
	}
	if c2 != c {
		//logger.Warn("[BetTower] not equal", zap.Int("C", c), zap.Int("C2", c2))
		return false
	}
	insertData := w.genStmInsertData(bets, payouts)
	err = dao.CreateBetBatch(insertData) // 批量生成流水
	if err != nil {
		logger.Error("[BetTower] save bet fail on CreateBetBatch", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}

	return true
}

// processAvtBetLoseSettle 小飞机输的结算
func (w *StmManager) processAvtBetLoseSettle(routeKey string, data []byte) bool {
	ss := &statement.SettleStmList{}
	err := proto.Unmarshal(data, ss)
	if err != nil {
		logger.Error("[BetTower] consume avt bet lose settle fail on proto.Unmarshal", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}
	ids, c := w.genSettleSqlIds(ss)
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	// TODO 批量查询投注日志
	bets, c1, e1 := dao.GetPlayBetIn(ids)
	if e1 != nil {
		logger.Error("[BetTower] consume avt bet lose settle fail on GetPlayBetIn", zap.String("Subject", routeKey), zap.Error(e1))
		return false
	}
	if c1 != c {
		return false
	}
	// 补足轮次信息
	payouts, e2 := w.fixAvtBetLoseResult(ss, bets)
	if e2 != nil {
		return false
	}
	insertData := w.genStmInsertData(bets, payouts)
	err = dao.CreateBetBatch(insertData) // 批量生成流水
	if err != nil {
		logger.Error("[BetTower] save avt bet lose fail on CreateBetBatch", zap.String("Subject", routeKey), zap.Error(err))
		return false
	}

	return true
}

func (w *StmManager) genSettleSqlIds(data *statement.SettleStmList) (string, int) {
	var idsArr []string
	ck := make(map[uint64]bool)
	c := 0
	for _, item := range data.States {
		if _, ok := ck[item.BetId]; ok {
			continue
		}
		ck[item.BetId] = true
		c++
		idsArr = append(idsArr, strconv.FormatUint(item.BetId, 10))
	}
	ids := strings.Join(idsArr, ",")
	return ids, c
}

// fixAvtBetLoseResult 小飞机输的补足轮次信息
func (w *StmManager) fixAvtBetLoseResult(data *statement.SettleStmList, bets map[uint64]*statement.PlayBetEnt) (map[uint64]*statement.PlayPayoutEnt, error) {
	ret := make(map[uint64]*statement.PlayPayoutEnt)
	t := dateutil.UTC()
	for _, item := range data.States {
		avBet := &statement.AviatorBetInfo{}
		bet := bets[item.BetId]
		err := json.Unmarshal(byteutil.StringToSliceByte(bet.Detail), avBet)
		if err != nil {
			logger.Error("[BetTower] fix avt bet info fail on json.Unmarshal aviator bet", zap.Uint64("BetId", item.BetId), zap.Error(err))
			return nil, err
		}
		avBet.Result = item.Multiplier
		b, e := json.Marshal(avBet)
		if err != nil {
			logger.Error("[BetTower] fix avt bet info fail on json.Marshal aviator bet", zap.Uint64("BetId", item.BetId), zap.Error(e))
			return nil, err
		}
		ret[bet.BetId] = &statement.PlayPayoutEnt{
			BetId:   bet.BetId,
			RoundId: bet.RoundId,
			GAct:    mcomm.GameAction_GameLoseSettle,
			Win:     mcomm.GameResult_GameLose,
			WinAmt:  "0",
			Payout:  "0",
			PayTime: data.Dateline,
			Op:      data.Op,
			Detail:  byteutil.SliceByteToString(b),
			Created: t,
			Updated: t,
		}
	}
	return ret, nil
}

func (w *StmManager) genStmInsertData(bets map[uint64]*statement.PlayBetEnt, payouts map[uint64]*statement.PlayPayoutEnt) []*statement.BetResult {
	var ret []*statement.BetResult
	t := dateutil.UTC()
	for _, playBet := range bets {
		playPayout := payouts[playBet.BetId]
		stmEnt := &statement.BetResult{
			BetId:   playBet.BetId,
			RoundId: playPayout.RoundId,
			Uid:     playBet.Uid,
			Uname:   playBet.Uname,
			AgtId:   playBet.AgtId,
			CurrId:  playBet.CurrId,
			InstId:  playBet.InstId,
			GameId:  playBet.GameId,
			Curr:    playBet.Curr,
			FreeBet: playBet.FreeBet,
			Win:     playPayout.Win,
			BetAmt:  playBet.BetAmt,
			EffAmt:  playBet.EffAmt,
			Fee:     playBet.Fee,
			WinAmt:  playPayout.WinAmt,
			Payout:  playPayout.Payout,
			BetTime: playBet.BetTime,
			PayTime: playPayout.PayTime,
			AgtUid:  playBet.AgtUid,
			Status:  mcomm.AcStatus_Effect,
			GAct:    playPayout.GAct,
			Op:      playPayout.Op,
			NodeId:  playBet.NodeId,
			Detail:  playPayout.Detail,
			Created: t,
			Updated: t,
		}
		ret = append(ret, stmEnt)
	}
	return ret
}

// genStatement deprecated
func (w *StmManager) genStatement(betId uint64) bool {
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	playBet, err := dao.GetPlayBet(betId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[BetTower] gen statement fail on GetPlayBet", zap.Uint64("BetId", betId), zap.Error(err))
			return false
		}
	}
	if playBet == nil || playBet.BetId == 0 {
		return false
	}
	playPayout, err1 := dao.GetPlayPayout(betId)
	if err1 != nil {
		if err1 != sql.ErrNoRows {
			logger.Error("[BetTower] gen statement fail on GetPlayPayout", zap.Uint64("BetId", betId), zap.Error(err1))
			return false
		}
	}
	if playPayout == nil || playPayout.BetId == 0 {
		return false
	}
	t := dateutil.UTC()
	stmEnt := &statement.BetResult{
		BetId:   playBet.BetId,
		RoundId: playPayout.RoundId,
		Uid:     playBet.Uid,
		Uname:   playBet.Uname,
		AgtId:   playBet.AgtId,
		CurrId:  playBet.CurrId,
		InstId:  playBet.InstId,
		GameId:  playBet.GameId,
		Curr:    playBet.Curr,
		FreeBet: playBet.FreeBet,
		Win:     playPayout.Win,
		BetAmt:  playBet.BetAmt,
		EffAmt:  playBet.EffAmt,
		Fee:     playBet.Fee,
		WinAmt:  playPayout.WinAmt,
		Payout:  playPayout.Payout,
		BetTime: playBet.BetTime,
		PayTime: playPayout.PayTime,
		AgtUid:  playBet.AgtUid,
		Status:  mcomm.AcStatus_Effect,
		GAct:    playPayout.GAct,
		Op:      playPayout.Op,
		NodeId:  playBet.NodeId,
		Detail:  playPayout.Detail,
		Created: t,
		Updated: t,
	}

	err = dao.CreateBet(stmEnt)
	if err != nil {
		logger.Error("[BetTower] save bet result fail on CreateBet", zap.Uint64("BetId", betId), zap.Error(err))
		return false
	}

	return true
}

// QueryStatements 查询流水数据 按版本，可增加时间段
func (w *StmManager) QueryStatements(req *statement.StmReq) (int16, []*statement.BetResult) {
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	var rs []*statement.BetResult
	var err error
	if req.Start > 0 {
		rs, err = dao.GetStatementsByStamp(req.AgtId, req.Start, req.End, req.VIdx, req.Per)
	} else {
		rs, err = dao.GetStatementsByVer(req.AgtId, req.VIdx, req.Per)
	}
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[BetTower] query statements Error on GetStatements...", zap.Int64("AgtId", req.AgtId), zap.Int32("Per", req.Per), zap.Int64("Start", req.Start), zap.Int64("End", req.End), zap.Error(err))
			return response.ErrInternal, nil
		}
		return 0, rs
	}

	return 0, rs
}

func (w *StmManager) QueryUserStatements(req *statement.StmUserReq) (int16, []*statement.BetResultSimple) {
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	rs, err := dao.GetStatementsByLimit(req.Uid, req.InstId, req.Offset, req.Limit, req.CurrId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[BetTower] query user statements Error on GetStatementsByLimit", zap.Uint64("Uid", req.Uid), zap.Int64("InstId", req.InstId), zap.Error(err))
			return response.ErrInternal, nil
		}
		return 0, rs
	}

	return 0, rs
}

func (w *StmManager) QueryUserStatement(req *statement.StmBetReq) (int16, *statement.BetResultSimple) {
	dao := stmdao.StmDao{Driver: w.stmDs.Ds()}
	rs, err := dao.GetStatementDone(req.BetId, req.Uid)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[BetTower] query user statement Error on GetStatementDone", zap.Uint64("BetId", req.BetId), zap.Error(err))
			return response.ErrInternal, nil
		}
		return 0, rs
	}

	return 0, rs
}

func (w *StmManager) QueryAviatorRoundFairness(req *aviatorp.AvtRoundFairnessReq) (int16, *aviatorp.AviatorRound) {
	var sb strings.Builder
	sb.WriteString(gloader.EnvString())
	sb.WriteString(constant.AvtFlewKey)
	sb.WriteString(strconv.FormatInt(req.InstId, 10))
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	avr, err := gloader.CommMgr().RedisPool().Get().HGet(ctx, sb.String(), strconv.FormatInt(req.RoundId, 10)).Result()
	cancel()
	if avr != "" {
		flew := &aviatorp.AviatorRound{}
		err = proto.Unmarshal(byteutil.StringToSliceByte(avr), flew)
		if err != nil {
			logger.Error("[BetTower] roundFairness query fail on unmarshal", zap.Int64("InstID", req.InstId), zap.Int64("RoundId", req.RoundId), zap.Error(err))
			return response.ErrInternal, nil
		}
		return 0, flew
	}

	dao := aviatordao.AviatorDao{Driver: w.avtDs.Ds()}
	ret, e1 := dao.GetAvtRound(req.RoundId, req.InstId)
	if e1 != nil {
		if e1 != sql.ErrNoRows {
			logger.Error("[BetTower] query aviator round entity Error on GetAvtRound", zap.Int64("InstID", req.InstId), zap.Int64("RoundId", req.RoundId), zap.Error(e1))
			return response.ErrInternal, nil
		}

		return response.ErrNotFound, nil
	}
	first3 := &aviatorp.First3Bets{}
	if ret.Clientseeds != nil {
		err = json.Unmarshal(ret.Clientseeds, first3)
		if err != nil {
			logger.Error("[BetTower] gen aviator flew fail on First3Bets.Unmarshal", zap.Int64("InstID", req.InstId), zap.Int64("RoundId", req.RoundId), zap.Error(err))
			return response.ErrInternal, nil
		}
	}
	flew := &aviatorp.AviatorRound{
		RoundId:  ret.Rid,
		Start:    ret.Start,
		End:      ret.End,
		State:    ret.Status,
		Seed:     ret.Sseed,
		First3:   first3,
		H512:     ret.H512,
		Result:   ret.Result,
		Hhex:     ret.Hhex,
		Hdecimal: ret.Hdecimal,
		Seed256:  ret.Sseed256,
	}

	return 0, flew
}

func (w *StmManager) QueryRouletteRoundFairness(req *roulettep.RouRoundReq) (int16, *roulettep.RouRound) {
	var sb strings.Builder
	sb.WriteString(gloader.EnvString())
	sb.WriteString(constant.RouFlewKey)
	sb.WriteString(strconv.FormatInt(req.InstId, 10))
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	avr, err := gloader.CommMgr().RedisPool().Get().HGet(ctx, sb.String(), strconv.FormatInt(req.RoundId, 10)).Result()
	cancel()
	if avr != "" {
		flew := &roulettep.RouRound{}
		err = proto.Unmarshal(byteutil.StringToSliceByte(avr), flew)
		if err != nil {
			logger.Error("[BetTower] roulette roundFairness query fail on unmarshal", zap.Int64("InstID", req.InstId), zap.Int64("RoundId", req.RoundId), zap.Error(err))
			return response.ErrInternal, nil
		}
		return 0, flew
	}

	dao := roudao.RouletteDao{Driver: w.rouDs.Ds()}
	ret, e1 := dao.GetRouRound(req.RoundId, req.InstId)
	if e1 != nil {
		if e1 != sql.ErrNoRows {
			logger.Error("[BetTower] query roulette round entity Error on GetAvtRound", zap.Int64("InstID", req.InstId), zap.Int64("RoundId", req.RoundId), zap.Error(e1))
			return response.ErrInternal, nil
		}

		return response.ErrNotFound, nil
	}
	first3 := &roulettep.First3Bets{}
	if ret.Clientseeds != nil {
		err = json.Unmarshal(ret.Clientseeds, first3)
		if err != nil {
			logger.Error("[BetTower] gen roulette flew fail on First3Bets.Unmarshal", zap.Int64("InstID", req.InstId), zap.Int64("RoundId", req.RoundId), zap.Error(err))
			return response.ErrInternal, nil
		}
	}
	flew := &roulettep.RouRound{
		RoundId:  ret.Rid,
		Start:    ret.Start,
		End:      ret.End,
		State:    ret.Status,
		Seed:     ret.Sseed,
		First3:   first3,
		H512:     ret.H512,
		RetNum:   ret.RetNum,
		RetColor: ret.RetColor,
		RetGrp:   ret.RetGrp,
		Seed256:  ret.Sseed256,
	}

	return 0, flew
}

func (w *StmManager) Destroy() {
	if w.betsChan != nil {
		close(w.betsChan)
	}
	if w.avBetsChan != nil {
		close(w.avBetsChan)
	}
	if w.settleChan != nil {
		close(w.settleChan)
	}
	w.cancel()
	if w.delayQue != nil {
		w.delayQue.StopConsume()
	}
}
